/*
 * @Author       : mark
 * @Date         : 2020-06-26
 * @copyleft Apache 2.0
 */ 
#include "buffer.h"

Buffer::Buffer(int initBuffSize) : buffer_(initBuffSize), readPos_(0), writePos_(0) {}

size_t Buffer::ReadableBytes() const { // 可读数据空间大小(待读数据)
    return writePos_ - readPos_;
}
size_t Buffer::WritableBytes() const { // 可写数据空间大小(剩余空间)
    return buffer_.size() - writePos_;
}

size_t Buffer::PrependableBytes() const { // 已读可重复利用空间大小[0, readPos_)]
    return readPos_;
}

const char* Buffer::Peek() const { // 指针指向可读数据的起始地址
    return BeginPtr_() + readPos_;
}

void Buffer::Retrieve(size_t len) { // 释放已处理的数据空间
    assert(len <= ReadableBytes());
    readPos_ += len;
}

void Buffer::RetrieveUntil(const char* end) { // 读取直到指定位置（end），常用于解析到特定分隔符（如 \r\n）。
    assert(Peek() <= end );
    Retrieve(end - Peek());
}

void Buffer::RetrieveAll() { // 清空缓冲区并重置位置
    bzero(&buffer_[0], buffer_.size());
    readPos_ = 0;
    writePos_ = 0;
}

std::string Buffer::RetrieveAllToStr() { // 将所有可读数据转为字符串并清空缓冲区
    std::string str(Peek(), ReadableBytes());
    RetrieveAll();
    return str;
}

const char* Buffer::BeginWriteConst() const { // 指针指向可写数据的起始地址,不可写
    return BeginPtr_() + writePos_;
}

char* Buffer::BeginWrite() { // 指针指向可写数据的起始地址,可写
    return BeginPtr_() + writePos_;
}

void Buffer::HasWritten(size_t len) { // 更新 writePos_，表示已写入 len 字节
    writePos_ += len;
} 

void Buffer::Append(const std::string& str) {
    Append(str.data(), str.length());
}

void Buffer::Append(const void* data, size_t len) {
    assert(data);
    Append(static_cast<const char*>(data), len);
}

void Buffer::Append(const char* str, size_t len) {
    assert(str);
    EnsureWriteable(len);
    std::copy(str, str + len, BeginWrite());
    HasWritten(len);
}

void Buffer::Append(const Buffer& buff) {
    Append(buff.Peek(), buff.ReadableBytes());
}

void Buffer::EnsureWriteable(size_t len) { // 确保有足够空间写入 len 字节，若不足则调用 MakeSpace_。
    if(WritableBytes() < len) {
        MakeSpace_(len);
    }
    assert(WritableBytes() >= len);
}

ssize_t Buffer::ReadFd(int fd, int* saveErrno) {
    // 将 fd 读取的数据追加到缓冲区。
    char buff[65535];
    struct iovec iov[2];
    const size_t writable = WritableBytes();
    /* 分散读， 保证数据全部读完 */
    iov[0].iov_base = BeginPtr_() + writePos_; // 可写地址
    iov[0].iov_len = writable;
    iov[1].iov_base = buff; // 临时缓冲区地址
    iov[1].iov_len = sizeof(buff);

    const ssize_t len = readv(fd, iov, 2); // 分散读，先把数据放到buffer_里，溢出了之后放到buff里
    if(len < 0) {
        *saveErrno = errno;
    }
    else if(static_cast<size_t>(len) <= writable) { // 读入的数据全部放入缓冲区
        writePos_ += len;
    }
    else { // 读入的数据大于可写空间，分散读，将数据分成两部分，一部分放入缓冲区，一部分放入临时缓冲区
        writePos_ = buffer_.size();
        Append(buff, len - writable); // 缓冲区溢出了之后，扩容，再把溢出来的数据添加到缓冲区里。避免了开始的时候要对fd里的数据大小进行判断。
    }
    return len;
}

ssize_t Buffer::WriteFd(int fd, int* saveErrno) {
    // 将缓冲区可读数据（readPos_ 到 writePos_）写入 fd。
    size_t readSize = ReadableBytes();
    ssize_t len = write(fd, Peek(), readSize);
    if(len < 0) {
        *saveErrno = errno;
        return len;
    } 
    readPos_ += len;
    return len;
}

char* Buffer::BeginPtr_() {
    return &*buffer_.begin();
}

const char* Buffer::BeginPtr_() const {
    return &*buffer_.begin();
}

void Buffer::MakeSpace_(size_t len) {
    if(WritableBytes() + PrependableBytes() < len) { // 可写+重用 不足 len 字节，扩容
        buffer_.resize(writePos_ + len + 1); // 扩容到大小为 当前写入的数据大小(BeginWrite()之前) + len + 1
    } 
    else { // 够用，将未读数据移到缓冲区头部，留出足够的空间写入
        size_t readable = ReadableBytes();
        std::copy(BeginPtr_() + readPos_, BeginPtr_() + writePos_, BeginPtr_()); // 将 [readPos_, writePos_) 的数据复制到缓冲区开头（BeginPtr_()）
        readPos_ = 0;
        writePos_ = readPos_ + readable;
        assert(readable == ReadableBytes());
    }
}